﻿using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Roslyn.Compilers.Common;
using Roslyn.Compilers.CSharp;
using RoslynHelper;

namespace OperatorGenericConstraint
{
    internal class GenericMethodCallRewriter : SyntaxRewriter
    {
        private ISemanticModel _semanticModel;
        private Dictionary<MethodDeclarationSyntax, List<SyntaxKind>> _methodsWithOperators = new Dictionary<MethodDeclarationSyntax, List<SyntaxKind>>();

        public GenericMethodCallRewriter(ISemanticModel semanticModel, Dictionary<MethodDeclarationSyntax, List<SyntaxKind>> methodsWithOperators)
        {
            _semanticModel = semanticModel;
            _methodsWithOperators = methodsWithOperators;
        }

        protected override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
        {
            var sm = _semanticModel.GetSemanticInfo(node.Expression);
            MethodSymbol ms;
            if (sm.Symbol == null && sm.CandidateSymbols.Count == 1 && (ms = sm.CandidateSymbols[0] as MethodSymbol) != null)
            {
                foreach (var methodWithOperatorsKVP in _methodsWithOperators)
                {
                    var methodWithOperators = methodWithOperatorsKVP.Key;
                    if (methodWithOperators.Identifier.GetText() == ms.Name && ((NamespaceDeclarationSyntax)methodWithOperators.Parent.Parent).Name.GetText() == ms.ContainingType.ContainingNamespace.Name && ((ClassDeclarationSyntax)methodWithOperators.Parent).Identifier.GetText() == ms.ContainingType.Name)
                    {
                        return node.AddParameters(methodWithOperatorsKVP.Value.Select(sk => 
                            Syntax.Argument(
                                expression: Syntax.ParenthesizedLambdaExpression(
                                    Syntax.ParameterList(
                                        parameters: SeparatedList.Create<ParameterSyntax>(
                                            Syntax.Parameter(
                                                identifier: Syntax.Identifier("e1")),
                                            Syntax.Parameter(
                                                identifier: Syntax.Identifier("e2")))), 
                                    body: Syntax.BinaryExpression(
                                        sk,
                                        Syntax.IdentifierName("e1"),
                                        right: Syntax.IdentifierName("e2"))))));
                    }
                }
            }
            return base.VisitInvocationExpression(node);
        }
    }
}
